<template>
  <div class="m-snakerflow-designer">
    <div id="LF-view"></div>
    <PropertySetting ref="propertySetting" :node="nodeClick" v-model="processForm" :lf="lf"/>
    <el-dialog
        :title="dialogTitle"
        :visible.sync="dialogVisible"
        destroy-on-close
        width="60%"
        :before-close="handleClose">
        <component :is="dialogType" :graphData="graphData" ref="dialogComponent"></component>
        <span slot="footer" class="dialog-footer" v-if="dialogType=='ImportData' || dialogType=='HighLightData'">
            <el-button @click="handleClose">取 消</el-button>
            <el-button type="primary" @click="handleSubmit">确 定</el-button>
            <el-dropdown placement="top" trigger="click" style="float: left;" v-if="localStorageLfData.length" @command="handleCommand">
              <el-button type="text">选择<i class="el-icon-arrow-down el-icon--right"></i></el-button>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item :key="item.value" v-for="item in localStorageLfData" :command="item.value">
                  {{item.label}}&nbsp;&nbsp;<i class="el-icon-remove" @click="handleRemove(item.value)"></i>
                </el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
        </span>
        <span slot="footer" class="dialog-footer" v-if="dialogType=='DataDetail'">
            <el-button type="primary" class="m-btn-copy" :data-clipboard-text="copyJsonContent">复制JSON</el-button>
            <el-button type="primary" class="m-btn-copy" :data-clipboard-text="copyXmlContent">复制XML</el-button>
        </span>
        </el-dialog>
  </div>
</template>

<script>
// @ is an alias to /src
import LogicFlow from '@logicflow/core'
import Task from '@/components/snakerflow/task'
import Transition from '@/components/snakerflow/transition'
import Custom from '@/components/snakerflow/custom'
import Start from '@/components/snakerflow/start'
import End from '@/components/snakerflow/end'
import Decision from '@/components/snakerflow/decision'
import Join from '@/components/snakerflow/join'
import Fork from '@/components/snakerflow/fork'
import PropertySetting from '@/components/PropertySetting'
import DataDetail from './control/DataDetail.vue'
import ImportData from './control/ImportData.vue'
import HighLightData from './control/HighLightData.vue'

import { Snapshot, DndPanel, SelectionSelect, Menu, Control } from '@logicflow/extension'
import '@logicflow/core/dist/style/index.css'
import '@logicflow/extension/lib/style/index.css'
import { snakerXml2LogicFlowJson, logicFlowJsonToSnakerXml } from '@/components/snakerflow/tool'
import Clipboard from 'clipboard'
import { Message } from 'element-ui'
export default {
  name: 'SnakerFlowDesigner',
  components: {
    PropertySetting,
    DataDetail,
    ImportData,
    HighLightData
  },
  props: {
    value: {
      type: Object,
      default () {
        return {}
      }
    },
    config: {
      type: Object,
      default () {
        return {
          grid: true
        }
      }
    },
    viewer: { // 预览模式
      type: Boolean,
      default: false
    },
    highLight: {
      type: Object,
      default () {
        return {
        }
      }
    }
  },
  data () {
    return {
      lf: null,
      nodeClick: null,
      dialogTitle: '',
      dialogVisible: false,
      dialogType: 'DataDetail',
      graphData: {},
      clipboard: null,
      processForm: {},
      refreshLfData: 1
    }
  },
  watch: {
    value: {
      handler (n) {
        if (n && n.nodes && this.lf) {
          // this.init()
          this.initProcessForm(n)
          this.lf.render(n)
        }
      },
      deep: true
    },
    processForm: {
      handler (n) {
        if (n) {
          delete n.type
          this.$emit('input', n)
        }
      },
      deep: true
    },
    highLight: {
      handler (n) {
        this.setHighLight(n)
      },
      deep: true
    }
  },
  computed: {
    copyJsonContent () {
      if (this.dialogType === 'DataDetail' && this.lf) {
        return JSON.stringify({
          ...this.value,
          ...this.lf.getGraphData()
        }, null, 2)
      }
      return ''
    },
    copyXmlContent () {
      if (this.dialogType === 'DataDetail' && this.lf) {
        return logicFlowJsonToSnakerXml({
          ...this.value,
          ...this.lf.getGraphData()
        })
      }
      return ''
    },
    localStorageLfData () {
      const res = []
      if (this.dialogType === 'ImportData' && [1, 2].includes(this.refreshLfData)) {
        Object.keys(window.localStorage).forEach(key => {
          if (key.startsWith('LFDATA###')) {
            const arr = key.split('###')
            res.push({
              value: key,
              label: arr[2]
            })
          }
        })
      }
      return res
    }
  },
  mounted () {
    this.init()
    if (this.value) {
      this.initProcessForm(this.value)
      this.lf.render(this.value)
    }
    this.clipboard = new Clipboard('.m-btn-copy')
    this.clipboard.on('success', function (e) {
      Message.success('复制成功')
      e.clearSelection()
    })

    this.clipboard.on('error', function (e) {
      Message.error('复制失败')
    })
  },
  methods: {
    /**
     * 初始化流程定义表单信息
     */
    initProcessForm (data) {
      this.$set(this.processForm, 'type', 'snaker:process')
      this.$set(this.processForm, 'name', data.name)
      this.$set(this.processForm, 'displayName', data.displayName)
      this.$set(this.processForm, 'expireTime', data.expireTime)
      this.$set(this.processForm, 'instanceUrl', data.instanceUrl)
      this.$set(this.processForm, 'instanceNoClass', data.instanceNoClass)
    },
    init () {
      // 画布配置
      LogicFlow.use(Snapshot)
      LogicFlow.use(DndPanel)
      LogicFlow.use(SelectionSelect)
      LogicFlow.use(Menu)
      LogicFlow.use(Control)
      const defaultConfig = {}
      if (this.viewer) {
        defaultConfig.isSilentMode = true
      }
      const lf = new LogicFlow({ ...this.config, ...defaultConfig, container: document.querySelector('#LF-view') })
      this.lf = lf
      // 注册自定义元素
      this.lf.register(Task)
      this.lf.register(Transition)
      this.lf.register(Custom)
      this.lf.register(Start)
      this.lf.register(End)
      this.lf.register(Decision)
      this.lf.register(Join)
      this.lf.register(Fork)
      // 初始化操作栏
      this.initOp(this.lf)
      // 初始化事件
      this.initEvent(lf)
    },
    initOp (lf) {
      if (this.viewer) return
      // 设置拖拽面板
      this.lf.extension.dndPanel.setPatternItems([
        {
          type: 'snaker:start',
          text: '开始',
          label: '开始节点',
          properties: {},
          icon: ''
        },
        {
          type: 'snaker:task',
          text: '用户任务',
          label: '用户任务',
          properties: {},
          icon: '',
          className: 'important-node'
        },
        {
          type: 'snaker:custom',
          text: '自定义任务',
          label: '自定义任务',
          properties: {},
          icon: '',
          className: 'import_icon'
        },
        {
          type: 'snaker:decision',
          label: '条件判断',
          properties: {},
          icon: ''
        },
        {
          type: 'snaker:fork',
          label: '分支',
          properties: {},
          className: 'lf-control-fork'
        },
        {
          type: 'snaker:join',
          label: '合并',
          properties: {},
          className: 'lf-control-join'
        },
        {
          type: 'snaker:end',
          text: '结束',
          label: '结束节点',
          properties: {},
          icon: ''
        }
      ])
      // 设置右键菜单
      this.lf.extension.menu.setMenuConfig({
        nodeMenu: [
          {
            text: '删除',
            callback (node) {
            // node为该节点数据
              lf.deleteNode(node.id)
            }
          }
        ]
      })
      // 控制面板-清空画布
      this.lf.extension.control.addItem({
        iconClass: 'lf-control-clear',
        title: 'clear',
        text: '清空',
        onClick: (lf, ev) => {
          lf.clearData()
        }
      })
      // 控制面板-添加查看按钮
      this.lf.extension.control.addItem({
        iconClass: 'lf-control-see',
        title: '',
        text: '查看',
        onClick: (lf, ev) => {
          this.graphData = {
            ...this.value,
            ...lf.getGraphData()
          }
          this.$nextTick(() => {
            this.dialogTitle = '查看流程数据'
            this.dialogVisible = true
            this.dialogType = 'DataDetail'
          })
        }
      })
      // 控制面板-添加导入按钮
      this.lf.extension.control.addItem({
        iconClass: 'lf-control-import',
        title: '',
        text: '导入',
        onClick: (lf, ev) => {
          this.dialogTitle = '导入流程数据(同时支持xml/json)'
          this.dialogVisible = true
          this.dialogType = 'ImportData'
        }
      })
      // 控制面板-设置高亮数据
      this.lf.extension.control.addItem({
        iconClass: 'lf-control-setting',
        title: '',
        text: '设置高亮',
        onClick: (lf, ev) => {
          this.dialogTitle = '导入高亮数据(json)'
          this.dialogVisible = true
          this.dialogType = 'HighLightData'
        }
      })
      // 控制面板-清空画布
      this.lf.extension.control.addItem({
        iconClass: 'lf-control-save',
        title: '',
        text: '保存',
        onClick: (lf, ev) => {
          this.graphData = lf.getGraphData()
          this.$nextTick(() => {
            const res = {
              ...this.value,
              ...this.graphData
            }
            this.$emit('on-save', {
              json: res,
              xml: logicFlowJsonToSnakerXml(res)
            })
          })
        }
      })
    },
    initEvent (lf) {
      if (this.viewer) return
      const { eventCenter } = this.lf.graphModel
      eventCenter.on('node:click', (args) => {
        this.nodeClick = args.data
        this.$nextTick(() => {
          this.$refs.propertySetting.show()
        })
      })
      eventCenter.on('edge:click', (args) => {
        this.nodeClick = args.data
        this.$nextTick(() => {
          this.$refs.propertySetting.show()
        })
      })
      eventCenter.on('edge:add', (args) => {
        // 修改边类型
        lf.changeEdgeType(args.data.id, 'snaker:transition')
      })
      eventCenter.on('blank:contextmenu', (args) => {
        this.nodeClick = {
          name: this.processForm.name,
          displayName: this.processForm.displayName,
          expireTime: this.processForm.expireTime,
          instanceUrl: this.processForm.instanceUrl,
          instanceNoClass: this.processForm.instanceNoClass,
          type: 'snaker:process'
        }
        this.$nextTick(() => {
          this.$refs.propertySetting.show()
        })
      })
    },
    handleClose () {
      this.dialogVisible = false
      this.$refs.dialogComponent.handleClose()
    },
    handleSubmit () {
      if (this.dialogType === 'ImportData') {
        this.lf.clearData()
        try {
          this.importJson(this.$refs.dialogComponent.graphJsonStr)
        } catch {
          this.importXml(this.$refs.dialogComponent.graphJsonStr)
        }
      } else if (this.dialogType === 'HighLightData') {
        this.setHighLight(JSON.parse(this.$refs.dialogComponent.highLightJsonStr))
      }
      this.dialogVisible = false
    },
    /**
     * 导入snakerxml
     */
    importXml (xml) {
      const data = snakerXml2LogicFlowJson(xml)
      this.initProcessForm(data)
      this.lf.render(data)
    },
    /**
     * 导入jsonstr
     */
    importJson (jsonStr) {
      const data = JSON.parse(jsonStr)
      this.initProcessForm(data)
      this.lf.render(data)
    },
    /**
     * 设置高亮数据
     * @param data { "historyNodeNames": [], "historyEdgeNames": [], "activeNodeNames": []}
     */
    setHighLight (data) {
      if (!this.lf || !data) return
      // 设置历史节点
      if ((data && data.historyNodeNames) || data.historyNodeNames.length) {
        data.historyNodeNames.forEach(nodeName => {
          this.lf.setProperties(nodeName, { state: 'history' })
        })
      }
      // 设置当前节点
      if ((data && data.activeNodeNames) || data.activeNodeNames.length) {
        data.activeNodeNames.forEach(nodeName => {
          this.lf.setProperties(nodeName, { state: 'active' })
        })
      }
      // 设置历史边
      if ((data && data.historyEdgeNames) || data.historyEdgeNames.length) {
        data.historyEdgeNames.forEach(edgeName => {
          const edgeModel = this.lf.getEdgeModelById(edgeName)
          if (edgeModel) {
            edgeModel.setProperties({ state: 'history' })
          }
        })
      }
    },
    handleCommand (key) {
      this.$refs.dialogComponent.graphJsonStr = window.localStorage.getItem(key)
    },
    handleRemove (key) {
      window.localStorage.removeItem(key)
      if (this.refreshLfData === 1) {
        this.refreshLfData = 2
      } else {
        this.refreshLfData = 1
      }
    }
  }
}
</script>
<style scoped>
.m-snakerflow-designer {
  height: 100vh;
  position: relative;
}
#LF-view{
  width: calc(100% - 100px);
  height: 80%;
  outline: none;
  margin-left: 50px;
}
</style>
<style>
.lf-control-see {
    background-image:url('');
}
.lf-control-import {
    background-image:url('');
}
.lf-control-clear {
  background-image:url('');
}
.lf-control-save {
  background-image:url('');
}
.lf-control-join {
  width: 70px;
  height: 55px;
  background-repeat: no-repeat;
  background-size: 35px 30px;
  background-position: center;
  background-image: url('');
}
.lf-control-fork {
  width: 70px;
  height: 55px;
  background-repeat: no-repeat;
  background-size: 35px 30px;
  background-position: center;
  background-image: url('');
}
.lf-control-setting {
  width: 70px;
  height: 55px;
  background-repeat: no-repeat;
  background-size: 35px 30px;
  background-position: center;
  background-image: url('');
}
</style>
